<template>
    <div class="graph2d-container">
        <svg ref="svg" :width="width" :height="height"></svg>
        <!-- <div class="legend">
            <div class="legend-title">Transformer</div>
            <div class="legend-item"><span class="legend-dot course"></span>course</div>
            <div class="legend-item"><span class="legend-dot group"></span>knowledge_group</div>
            <div class="legend-item"><span class="legend-dot point"></span>knowledge_point</div>
        </div> -->
        <div class="graph-title">深度学习入门：基于Python的理论与实现</div>
    </div>
</template>

<script setup>
    import { ref, onMounted, onBeforeUnmount } from 'vue'
    import * as d3 from 'd3'

    const { graphData } = defineProps({ graphData: Object })
    const width = ref(window.innerWidth)
    const height = ref(window.innerHeight)
    const svg = ref(null)
    let sim = null

    const colorMap = {
        course: '#2b61d6',
        group: '#5ec16d',
        point: '#f6c24a',
    }

    function renderGraph() {
        const svgEl = d3.select(svg.value)
        svgEl.selectAll('*').remove()

        // 画连线
        const link = svgEl.append('g')
            .attr('stroke', '#b5d0b6')
            .attr('stroke-width', 2)
            .selectAll('path')
            .data(graphData.links)
            .enter().append('path')
            .attr('marker-end', 'url(#arrow)')
            .attr('fill', 'none')
            .attr('stroke-opacity', 0.7)

        // 箭头
        svgEl.append('defs').append('marker')
            .attr('id', 'arrow')
            .attr('viewBox', '0 -5 10 10')
            .attr('refX', 22)
            .attr('refY', 0)
            .attr('markerWidth', 8)
            .attr('markerHeight', 8)
            .attr('orient', 'auto')
            .append('path')
            .attr('d', 'M0,-5L10,0L0,5')
            .attr('fill', '#b5d0b6')

        // 画节点
        const node = svgEl.append('g')
            .selectAll('circle')
            .data(graphData.nodes)
            .enter().append('circle')
            .attr('r', d => d.level === 0 ? 28 : d.level === 1 ? 22 : 18)
            .attr('fill', d => d.isMastered ? '#00ff00' : (d.level === 0 ? colorMap.course : d.level === 1 ? colorMap.group : colorMap.point))
            .attr('filter', 'url(#glow)')
            .attr('stroke', '#fff')
            .attr('stroke-width', 2)
            .call(d3.drag()
                .on('start', dragstarted)
                .on('drag', dragged)
                .on('end', dragended))

        // 发光滤镜
        svgEl.append('defs').append('filter')
            .attr('id', 'glow')
            .append('feDropShadow')
            .attr('dx', 0).attr('dy', 0)
            .attr('stdDeviation', 6)
            .attr('flood-color', '#b5d0b6')
            .attr('flood-opacity', 0.7)

        // 画节点文本
        svgEl.append('g')
            .selectAll('text')
            .data(graphData.nodes)
            .enter().append('text')
            .text(d => d.label)
            .attr('font-size', 16)
            .attr('fill', '#222')
            .attr('text-anchor', 'middle')
            .attr('dy', 5)
            .attr('pointer-events', 'none')

        sim.on('tick', () => {
            link.attr('d', d => {
                const sx = d.source.x, sy = d.source.y, tx = d.target.x, ty = d.target.y
                return `M${sx},${sy}L${tx},${ty}`
            })
            node.attr('cx', d => d.x).attr('cy', d => d.y)
            svgEl.selectAll('text')
                .attr('x', d => d.x)
                .attr('y', d => d.y)
        })

        function dragstarted(event, d) {
            if (!event.active) sim.alphaTarget(0.3).restart()
            d.fx = d.x
            d.fy = d.y
        }
        function dragged(event, d) {
            d.fx = event.x
            d.fy = event.y
        }
        function dragended(event, d) {
            if (!event.active) sim.alphaTarget(0)
            d.fx = null
            d.fy = null
        }
    }

    function resizeHandler() {
        width.value = window.innerWidth
        height.value = window.innerHeight
        if (sim) {
            sim.force('center', d3.forceCenter(width.value / 2, height.value / 2))
            sim.alpha(0.5).restart()
        }
        renderGraph()
    }

    onMounted(() => {
        sim = d3.forceSimulation(graphData.nodes)
            .force('link', d3.forceLink(graphData.links).id(d => d.id).distance(160))
            .force('charge', d3.forceManyBody().strength(-400))
            .force('center', d3.forceCenter(width.value / 2, height.value / 2))
            .force('collide', d3.forceCollide(50))
        renderGraph()
        const svgEl = d3.select(svg.value)
        svgEl.call(d3.zoom()
            .scaleExtent([0.2, 3])
            .on('zoom', (event) => {
                svgEl.selectAll('g').attr('transform', event.transform)
            })
        )
        window.addEventListener('resize', resizeHandler)
    })
    onBeforeUnmount(() => {
        window.removeEventListener('resize', resizeHandler)
        if (sim) sim.stop()
    })
</script>

<style scoped>
    .graph2d-container {
        width: 100vw;
        height: 100vh;
        background: #f7fafb;
        position: fixed;
        left: 0;
        top: 0;
        overflow: hidden;
        margin: 0;
        padding: 0;
    }

    svg {
        width: 100vw;
        height: 100vh;
        display: block;
        margin: 0;
        padding: 0;
    }

    .legend {
        position: absolute;
        top: 24px;
        left: 220px;
        background: #fff;
        border-radius: 8px;
        box-shadow: 0 2px 8px #0001;
        padding: 8px 18px 8px 18px;
        font-size: 15px;
        color: #222;
        z-index: 10;
        display: flex;
        align-items: center;
        gap: 12px;
    }

    .legend-title {
        font-weight: bold;
        margin-right: 10px;
    }

    .legend-item {
        display: flex;
        align-items: center;
        gap: 4px;
    }

    .legend-dot {
        display: inline-block;
        width: 14px;
        height: 14px;
        border-radius: 50%;
        margin-right: 3px;
    }

    .legend-dot.course {
        background: #2b61d6;
    }

    .legend-dot.group {
        background: #5ec16d;
    }

    .legend-dot.point {
        background: #f6c24a;
    }

    .graph-title {
        position: absolute;
        top: 18px;
        left: 30px;
        background: #fff;
        border-radius: 8px;
        box-shadow: 0 2px 8px #0001;
        padding: 10px 24px;
        font-size: 22px;
        font-weight: bold;
        color: #222;
        z-index: 12;
    }
</style>